package com.wuwei.elearning.question.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.wuwei.elearning.exception.ElearningMysqlException;
import com.wuwei.elearning.question.domain.*;
import com.wuwei.elearning.question.mapper.*;
import com.wuwei.elearning.question.service.QuestionItemService;
import com.wuwei.elearning.question.vo.GetResultVo;
import com.wuwei.elearning.question.vo.PageVo;
import com.wuwei.elearning.question.vo.QuestionVo;
import com.wuwei.elearning.question.vo.TestVo;
import com.wuwei.elearning.setting.domain.User;
import com.wuwei.elearning.setting.mapper.UserMapper;
import com.wuwei.elearning.utils.UUIDUtil;
import com.wuwei.elearning.utils.result.Result;
import com.wuwei.elearning.utils.token.TokenUtil;
import com.wuwei.elearning.websocket.WebSocketServer;
import lombok.SneakyThrows;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.time.Duration;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 每个单独的问题都会生成一个question_item 服务实现类
 * </p>
 *
 * @author wuwei
 * @since 2024-01-17
 */
@Service
public class QuestionItemServiceImpl extends ServiceImpl<QuestionItemMapper, QuestionItem> implements QuestionItemService {

    @Resource
    private QuestionMapper questionMapper;

    @Resource
    private QuestionItemMapper questionItemMapper;

    @Resource
    private UserMapper userMapper;



    @Resource
    private QuestionItemRaletionQuestionMapper questionItemRaletionQuestionMapper;

    @Resource
    private SubjectMapper subjectMapper;

    @Resource
    private WrongRecordMapper wrongRecordMapper;

    @Resource
    private CollectMapper collectMapper;

    /**
     *  根据出题的科目，比如是M1还是M2以及出题数量来出题，将出的题目添加到mysql数据库中
     *  1.在question_item表新增一条记录
     *  2.question_item_raletion_question关系表中新增多条记录
     * @param questionVo 请求数据
     * @param account 用户账号
     * @return
     */
    @SneakyThrows
    @Override
    @Transactional
    public Result<String> batchInsert(Long account, QuestionVo questionVo) {
        Long[] chapter = questionVo.getChapter();
        Integer time = questionVo.getTime();
        Long subjectId = questionVo.getSubjectId();
        Integer quesitonNum = questionVo.getNumber();
        Long gradeId = questionVo.getGradeId();
        String title = questionVo.getTitle();
        Integer expiredTime = questionVo.getExpiredTime();
        //这里不能出现班级，标题都一样的试卷
        LambdaQueryWrapper<QuestionItem> queryWrapper1 = new LambdaQueryWrapper<>();
        queryWrapper1.eq(QuestionItem::getTitle,title).eq(QuestionItem::getGradeId,gradeId);
        QuestionItem selectOne = questionItemMapper.selectOne(queryWrapper1);
        if (selectOne!=null) {
            throw new ElearningMysqlException(30034,"试卷重复！");
        }

        //todo 后面这个可以从缓存中获取
        LambdaQueryWrapper<User> queryWrapper2 = new LambdaQueryWrapper<>();
        queryWrapper2.eq(User::getAccount,account);
        String name = userMapper.selectOne(queryWrapper2).getName();

        //question_item_raletion_question关系表中新增多条记录
        List<Long> subjectIdList;
        //从中选取quesitonNum数量的题目
        if (chapter.length==0) {
            //获取subjectId的全部叶子节点
            subjectIdList = subjectMapper.getLeafsIdByParentId(subjectId);
        } else {
            //根据chapter获取question
            subjectIdList = Arrays.asList(chapter);
        }

        //获取到随机的question表的主键
        List<Long> selectedQuestions = questionMapper.myListRandom(subjectIdList,quesitonNum);
        String res = "";
        if (selectedQuestions.isEmpty()) {
            throw new ElearningMysqlException(30044,"题库中没有题目！出题失败！");
        }

        if (selectedQuestions.size()<quesitonNum) {
            res = "所选数量已超出题库最大数，已为您选择题库最大数量";
            quesitonNum = selectedQuestions.size();
        }
        QuestionItem questionItem = new QuestionItem();
        //在question_item表新增一条记录
        questionItem.setUserName(name);
        questionItem.setCreateBy(account);
        questionItem.setTestTime(time*60);
        questionItem.setCreateTime(LocalDateTime.now());
        LocalDateTime createTime = questionItem.getCreateTime();
        questionItem.setExpiredTime(createTime.plusDays(expiredTime));
        questionItem.setGradeId(gradeId);
        questionItem.setSubjectId(subjectId);
        questionItem.setTitle(title);
        questionItem.setNumber(quesitonNum);
        questionItemMapper.insert(questionItem);
        Long questionItemId = questionItem.getId();
        questionItemRaletionQuestionMapper.MyBatchInsert(questionItemId,selectedQuestions);
        /**
         * 向指定班级的用户推送试卷
         */
        LambdaQueryWrapper<User> queryWrapper3 = new LambdaQueryWrapper<>();
        queryWrapper3.eq(User::getGradeId,gradeId);
        List<User> users = userMapper.selectList(queryWrapper3);
        List<User> informUser = new ArrayList<>();
        for (User user : users) {
            String license = user.getLicense();
            if ("TA".equals(license)||"PA".equals(license)) {
                if ("TA".equals(license)){
                    //TA不考M6
                    if (subjectId != 6) {
                        informUser.add(user);
                    }
                } else {
                    //PA不考M5
                    if (subjectId != 5) {
                        informUser.add(user);
                    }
                }
            }else {
                if ("补差".equals(license)) {
                    String userSubject = user.getSubject();
                    if (userSubject!=null) {
                        List<Long> subjectIds = JSON.parseArray(userSubject, Long.class);
                        if (subjectIds.contains(subjectId)) {
                            informUser.add(user);
                        }
                    }
                } else {
                    log.error("user表的license出现了除了补差，TA，PA以外的属性，属性为"+license);
                }
            }
        }

        if (!informUser.isEmpty()) {
            for (User user : informUser) {
                Long account1 = user.getAccount();
                WebSocketServer.sendInfo("收到一份试卷，试卷标题是："+title,account1);
            }
        }

        return  Result.ok("设置成功!"+res);
    }

    @Resource
    private TestResultMapper testResultMapper;

    @Resource
    private TestAnswersMapper testAnswersMapper;


    @Override
    public Result<Map<String, Object>> batchGet(Long questionItemId, Long account) {
        if (checkIfHasTested(questionItemId, account)) {
            throw new ElearningMysqlException(30001,"您已经考过了！请不要重复考试");
        }
        QuestionItem questionItem = questionItemMapper.selectById(questionItemId);
        //如果查出来无记录，表明题目编号不对
        if (questionItem==null) {
            throw new ElearningMysqlException(30002,"题目编号不正确！");
        }
        LocalDateTime expiredTime = questionItem.getExpiredTime();
        if (expiredTime.isBefore(LocalDateTime.now())) {
            throw new ElearningMysqlException(30002,"该考卷已经过期！");
        }

        Integer testTime = questionItem.getTestTime();
        LambdaQueryWrapper<QuestionItemRaletionQuestion> queryWrapper1 = new LambdaQueryWrapper<>();
        queryWrapper1.eq(QuestionItemRaletionQuestion::getQuestionItemId,questionItemId);
        List<QuestionItemRaletionQuestion> selectList = questionItemRaletionQuestionMapper.selectList(queryWrapper1);
        List<Question> result = new ArrayList<>();
        selectList.forEach(s->{
            Long questionId = s.getQuestionId();
            Question question = questionMapper.selectById(questionId);
            result.add(question);
        });
        Map<String,Object> map = new HashMap<>();
        map.put("result",result);
        map.put("time",testTime);
        return Result.ok(map);
    }

    /**
     * 学员查看考卷和教员查看考卷唯一的不同就在于学员只有完成过考试以后才可以查看考卷，而教员不用
     * @param questionItemId
     * @return
     */
    @Override
    public Result<Map<String, Object>> getItem(Long questionItemId) {
        Map<String, Object> map = selectQuestions(questionItemId);
        return Result.ok(map);
    }


    /**
     * 1.添加一条考试记录到test_result表
     * 2.添加考卷到test_answer表
     * @param testVo
     * @param account
     * @return
     */
    @Override
    @Transactional
    public Result<String> submit(TestVo testVo, Long account) {
        //这里要防止重复提交试卷
        Long count = testResultMapper.selectCount(new LambdaQueryWrapper<TestResult>().eq(TestResult::getQuestionItemId, testVo.getQuestionItemId())
                .eq(TestResult::getAccount, account));
        if (count==1) {
            throw new ElearningMysqlException(30022,"您已经考过该试卷了！请不要重复考试！");
        }
        if (count > 1){
            throw new ElearningMysqlException(30022,"testResult表出现错误，根据question_item_id和account查出超过一条数据！");
        }
        Long questionItemId = testVo.getQuestionItemId();
        //1.添加一条考试记录到test_result表
        TestResult testResult = new TestResult();
        testResult.setAccount(account);
        testResult.setScore(testVo.getScore());
        testResult.setCreateTime(LocalDateTime.now());
        testResult.setQuestionItemId(questionItemId);
        testResult.setTitle(testVo.getTitle());
        // 将秒数转换为Duration对象
        Duration duration = Duration.ofSeconds(testVo.getSeconds());
        // 使用Duration对象创建LocalTime对象
        LocalTime usedTime = LocalTime.MIDNIGHT.plus(duration);
        testResult.setUsedTime(usedTime);
        testResult.setUsername(testVo.getName());
        int insert = testResultMapper.insert(testResult);
        if (insert==1) {
            //2.添加考卷到test_answers表
            List<List<String>> allAnswers = testVo.getAllAnswers();
            for (int i = 0; i < allAnswers.size(); i++) {
                List<String> a = allAnswers.get(i);
                TestAnswers testAnswers = new TestAnswers();
                testAnswers.setQuestionItemId(questionItemId);
                testAnswers.setUserName(testVo.getName());
                testAnswers.setUserId(account);
                List<String> collect = a.stream().filter(b->!"".equals(b)).collect(Collectors.toList());
                if (collect.size()==1) {
                    testAnswers.setSelected(collect.get(0));
                }
                if (collect.size()>1) {
                    String selected = String.join("+", collect);
                    testAnswers.setSelected(selected);
                }
                testAnswers.setQuestionId(testVo.getIds().get(i));
                testAnswersMapper.insert(testAnswers);
            }
            return Result.ok("您的得分是"+testVo.getScore());
        }
        throw new ElearningMysqlException(30001,"test_result表添加记录失败！");
    }

    @Override
    public List<QuestionItem> getResultList(PageVo<GetResultVo> resultVoPageVo) {
        int currentPage = resultVoPageVo.getCurrentPage();
        int pageSize = resultVoPageVo.getPageSize();
        IPage<QuestionItem> page = new Page<>();
        page.setCurrent(currentPage);
        page.setSize(pageSize);
        LambdaQueryWrapper<QuestionItem> queryWrapper = new LambdaQueryWrapper<>();
        GetResultVo params = resultVoPageVo.getParams();
        if (params==null) {
            params = new GetResultVo();
        }
        buildQuery(queryWrapper, params);

        IPage<QuestionItem> selectPage = questionItemMapper.selectPage(page, queryWrapper);
        return selectPage.getRecords();
    }

    /**
     * 构造查询条件
     * @param queryWrapper
     * @param params
     */
    private void buildQuery(LambdaQueryWrapper<QuestionItem> queryWrapper, GetResultVo params) {
        String title = params.getTitle();
        LocalDateTime startTime = params.getStartTime();
        LocalDateTime endTime = params.getEndTime();

        queryWrapper.like(!"".equals(title), QuestionItem::getTitle, title)
                .ge(startTime != null, QuestionItem::getCreateTime, startTime)
                .le(endTime != null, QuestionItem::getCreateTime, endTime)
                .orderByDesc(QuestionItem::getCreateTime);
    }

    @Override
    public Long getResultTotal(GetResultVo resultVoPageVo) {
        LambdaQueryWrapper<QuestionItem> queryWrapper = new LambdaQueryWrapper<>();

        buildQuery(queryWrapper, resultVoPageVo);

        Long count = questionItemMapper.selectCount(queryWrapper);
        return count;
    }

    /**
     * 学员查看考卷和教员查看考卷唯一的不同就在于学员只有完成过考试以后才可以查看考卷，而教员不用
     * @param questionItemId
     * @return
     */
    @Override
    public Result<Map<String, Object>> checkForStudent(Long questionItemId, Long account) {
        if (checkIfHasTested(questionItemId, account)) {
            Map<String, Object> map = selectQuestions(questionItemId);
            return Result.ok(map);
        } else {
            //如果在test_result表没有记录有两种可能，一种是questionItemId不正确，还有一种是真的没有考试过
            QuestionItem questionItem = questionItemMapper.selectById(questionItemId);
            //如果查出来无记录，表明题目编号不对
            if (questionItem == null) {
                throw new ElearningMysqlException(30002, "试卷编号不正确！");
            } else {
                throw new ElearningMysqlException(30003,"请先完成考试再查看试卷！");
            }
        }
    }

    /**
     * 删除question_item,question_item_raletion_question,test_answers,test_result四张表中有关记录
     * 该方法只有管理员或者老师才能访问
     * @param questionItemId
     * @param account
     * @return
     */
    @Override
    @Transactional
    public Result<String> delete(Long questionItemId, Long account) {
        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(User::getAccount,account);
        User user = userMapper.selectOne(queryWrapper);
        if (user.getStatus()==0||user.getStatus()==1) {
            //1删除question_item
            questionItemMapper.deleteById(questionItemId);
            //2删除question_item_raletion_question
            LambdaQueryWrapper<QuestionItemRaletionQuestion> wrapper = new LambdaQueryWrapper<>();
            wrapper.eq(QuestionItemRaletionQuestion::getQuestionItemId,questionItemId);
            questionItemRaletionQuestionMapper.delete(wrapper);
            //3删除test_answers
            LambdaQueryWrapper<TestAnswers> wrapper1 = new LambdaQueryWrapper<>();
            wrapper1.eq(TestAnswers::getQuestionItemId,questionItemId);
            testAnswersMapper.delete(wrapper1);
            //4删除test_result
            LambdaQueryWrapper<TestResult> wrapper2 = new LambdaQueryWrapper<>();
            wrapper2.eq(TestResult::getQuestionItemId,questionItemId);
            testResultMapper.delete(wrapper2);
            return Result.ok("删除成功！");
        } else {
            return Result.fail("对不起，您没有权限！");
        }
    }

    /**
     * 根据随堂练习的类型和科目获取相应的题目清单
     * @param type 支持的随堂练习有顺序练习，随机练习，错题练习，收藏练习
     * @return
     */
    @Override
    public List<Question> getLearnItems(String type, Long selectedId, Long account) {
        if ("顺序练习".equals(type)||"随机练习".equals(type)){
//            List<Question> questions = questionMapper.mySelect(selectedId);

            List<Long> subjectIds = subjectMapper.getLeafsIdByParentId(selectedId);
            LambdaQueryWrapper<Question> wrapper = new LambdaQueryWrapper<>();
            wrapper.in(!subjectIds.isEmpty(),Question::getSubjectId,subjectIds);
            List<Question> questions = questionMapper.selectList(wrapper);
            if (!"顺序练习".equals(type)) {//为随机练习
                Collections.shuffle(questions);
            }
            return questions;
        } else {
            if ("收藏练习".equals(type)) {
                // 这里在sql语句中进行了去重
                return questionMapper.mySelectCollect(account,selectedId);
            } else {//都不符合则为错题练习
                return questionMapper.mySelectWrong(account,selectedId);
            }
        }
    }

    /**
     * 删除question_item,question_item_raletion_question,test_answers,test_result四张表中有关记录
     * @param questionItems
     * @return
     */
    @Override
    @Transactional
    public Result<String> batchDelete(List<QuestionItem> questionItems,Long account) {
        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(User::getAccount,account);
        User user = userMapper.selectOne(queryWrapper);
        //这里只有管理员或者老师才可以删除考卷
        if (user.getStatus()!=0&&user.getStatus()!=1) {
            return Result.fail("对不起，您没有权限！");
        }
        questionItems.forEach(questionItem -> {
            Long questionItemId = questionItem.getId();
            //1删除question_item
            questionItemMapper.deleteById(questionItemId);
            //2删除question_item_raletion_question
            LambdaQueryWrapper<QuestionItemRaletionQuestion> wrapper = new LambdaQueryWrapper<>();
            wrapper.eq(QuestionItemRaletionQuestion::getQuestionItemId,questionItemId);
            questionItemRaletionQuestionMapper.delete(wrapper);
            //3删除test_answers
            LambdaQueryWrapper<TestAnswers> wrapper1 = new LambdaQueryWrapper<>();
            wrapper1.eq(TestAnswers::getQuestionItemId,questionItemId);
            testAnswersMapper.delete(wrapper1);
            //4删除test_result
            LambdaQueryWrapper<TestResult> wrapper2 = new LambdaQueryWrapper<>();
            wrapper2.eq(TestResult::getQuestionItemId,questionItemId);
            testResultMapper.delete(wrapper2);
        });
        return Result.ok("删除成功！");
    }

    /**
     * 查询学员还没有提交过的试卷
     */
    @Override
    public Map<String,Object> selectQuestionItems(String title, User user, Integer currentPage, Integer pageSize) {
        Long gradeId = user.getGradeId();
        Long account = user.getAccount();
        Map<String, Object> map = new HashMap<>();
        List<QuestionItem> list = new ArrayList<>();
        Integer count = 0;
        //当管理员或者老师访问时，直接返回空
        if (user.getStatus()==1||user.getStatus()==0) {
            map.put("records", list);
            map.put("total",count);
            return map;
        }
        if (gradeId !=null) {
            //PA不考M5，TA不考M6，补差则要看他的sujectId
            String license = user.getLicense();
            Integer skipPage = (currentPage-1)*pageSize;

            if ("TA".equals(license)||"PA".equals(license)) {
                Integer subjectId = "TA".equals(license)?6:5;
                title =  "_".equals(title)?null:title;
                list = questionItemMapper.selectNotSubmit(title, gradeId,account,subjectId,skipPage,pageSize);
                count = questionItemMapper.selectNotSubmitCount(title, gradeId,account,subjectId);
            } else {
                if ("补差".equals(license)) {
                    String subject = user.getSubject();
                    if (subject==null) {
                        throw new ElearningMysqlException(30035,"学员为补差时，必须设置subject信息！");
                    } else {
                        List<Long> subjectIds = JSON.parseArray(subject, Long.class);
                        if (subjectIds.size()==0) {
                            throw new ElearningMysqlException(30035,"user表的subject属性不合法!");
                        }
                        list = questionItemMapper.selectNotSubmitBySubject(title, gradeId,account,subjectIds,skipPage,pageSize);
                        count = questionItemMapper.selectNotSubmitCountBySubject(title, gradeId,account,subjectIds);

                    }
                } else if (license == null) {
                    throw new ElearningMysqlException(30035,"请尽快设置学员所学执照类型！");
                } else {
                    throw new ElearningMysqlException(30035,"user表的license只能为TA、PA、或补差！");
                }
            }
            map.put("records", list);
            map.put("total",count);
            return map;
        } else {
            throw new ElearningMysqlException(30044,"请尽快联系管理员设置您的班级信息！");
        }
    }

    /**
     * 查询该学员还有多少道试卷未做
     * @return
     */
    @Override
    public Integer getCountByAccount(User user) {
        Long gradeId = user.getGradeId();
        Long account = user.getAccount();
        if (gradeId==null||gradeId==0) {
            return 0;
        } else {
            //PA不考M5，TA不考M6，补差则要看他的sujectId
            String license = user.getLicense();
            if ("TA".equals(license)||"PA".equals(license)) {
                Integer subjectId = "TA".equals(license)?6:5;
                return questionItemMapper.selectNotSubmitCount(null,gradeId, account, subjectId);
            } else {
                if ("补差".equals(license)) {
                    String subject = user.getSubject();
                    if (subject!=null) {
                        List<Long> subjectIds = JSON.parseArray(subject, Long.class);
                        if (!subjectIds.isEmpty()) {
                            return questionItemMapper.selectNotSubmitCountBySubject(null, gradeId,account,subjectIds);
                        }
                    }
                }
                return 0;
            }
        }
    }

    /**
     * 根据questionItemId从question_item_raletion_question关系表中获取与question_item_id相关联的所有question_id，
     * 再从question_item表中根据id获取所有的Question
     * @param questionItemId
     * @return
     */
    private Map<String, Object> selectQuestions(Long questionItemId) {
        QuestionItem questionItem = questionItemMapper.selectById(questionItemId);
        //如果查出来无记录，表明题目编号不对
        if (questionItem == null) {
            throw new ElearningMysqlException(30002, "试卷编号不正确！");
        }
        List<Question> result = new ArrayList<>();
        LambdaQueryWrapper<QuestionItemRaletionQuestion> queryWrapper1 = new LambdaQueryWrapper<>();
        queryWrapper1.eq(QuestionItemRaletionQuestion::getQuestionItemId, questionItemId);
        List<QuestionItemRaletionQuestion> selectList = questionItemRaletionQuestionMapper.selectList(queryWrapper1);
        selectList.forEach(s -> {
            Long questionId = s.getQuestionId();
            Question questionForZhizhao = questionMapper.selectById(questionId);
            result.add(questionForZhizhao);
        });
        Map<String, Object> map = new HashMap<>();
        map.put("result", result);
        map.put("time", null);
        return map;
    }


    /**
     * 检查考生是否已经考过了的方法
     * @param questionItemId
     * @param account
     */
    private boolean checkIfHasTested(Long questionItemId, Long account) {
        LambdaQueryWrapper<TestResult> queryWrapper2 = new LambdaQueryWrapper<>();
        queryWrapper2.eq(TestResult::getAccount,account).eq(TestResult::getQuestionItemId,questionItemId);
        TestResult one = testResultMapper.selectOne(queryWrapper2);
        if (one != null) {//表明有记录，该考生考过
            return true;
        }return  false;
    }
}
